package com.sinai.mshab.client.util;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import com.google.gwt.user.client.rpc.IsSerializable;

/**
 * Object that stores answers from <code>QuestionComposite</code>s.
 * <code>QuestionComposite</code>s have field(s) which are filled in by the
 * user. These fields are our <i>answer</i> and we store these in the
 * <code>Answer</code> object.
 * 
 * @author Arthur Kalmenson
 * @see IsSerializable
 */
public class Answer implements IsSerializable {

	/**
	 * This field contains a map between a question and an answer. <b>Note:</b>
	 * The <code>HashMap</code> must contain Strings as its keys and values.
	 * 
	 * @gwt.typeArgs <java.lang.String, java.lang.String>
	 */
	private HashMap answers = new HashMap();

	/**
	 * The name of the database table where this <code>Answer</code> object
	 * belongs.
	 */
	private String tableName;

	/**
	 * Specifies whether or not this answer can be merged with other answers.
	 * 
	 */
	private boolean mergeable = true;

	/**
	 * Contains the where clauses for sql statements.
	 * 
	 * @gwt.typeArgs <com.sinai.mshab.client.util.WhereClause>
	 */
	private ArrayList where_clauses = new ArrayList();

	/**
	 * Adds a new answer using <code>fieldName</code> as the key and
	 * <code>value</code> as the value. Upon success, true is returned. If
	 * there was some error (i.e. empty field), false is returned.
	 * 
	 * @param fieldName
	 *            the field name in the database where <code>value</code> is
	 *            to be stored.
	 * @param value
	 *            the user inputed data.
	 * @return true if added successfully, false otherwise.
	 */
	public boolean add(String fieldName, String tableName, String value) {

		boolean added = false;

		/*
		 * validate the fieldName and value Strings to insure that they are only
		 * letters, numbers, underscores and hyphens.
		 */

		/*
		 * Regex used for validation. Checks to make sure that there are at
		 * least 2 of letters, numbers, underscores and hyphens and only allows
		 * those characters.
		 */
		final String CORRECT_FIELD = "^[\\w\\d-_]{2}(\\w+[\\d-_]*)*";

		// check fieldName and value.
		// if (fieldName.matches(CORRECT_FIELD)) {
		answers.put(fieldName, value);

		// flag that its been added successfully.
		added = true;
		// }

		// check the table name to make sure it's in the correct format.
		// if (tableName.matches(CORRECT_FIELD)) {
		this.tableName = tableName;
		// }

		return added;
	}

	/**
	 * Merges <i>this</i> <code>Answer</code> with <code>answer</code>,
	 * effectively adding <code>answer</code>. If there are conflicting key,
	 * value tuples, the tuple in <code>answer</code> will overwrite the
	 * existing value. Upon success, true is returned. If there was some error
	 * (i.e. empty field), false is returned.
	 * 
	 * @param answer
	 *            the new answer to add.
	 * @return true if added successfully, false otherwise.
	 */
	public void add(Answer answer) {
		answers.putAll(answer.getHashMap());
	}

	/**
	 * Returns the value (answer) that is associated with <code>fieldName</code>
	 * (question). If no such value/field exists, <code>null</code> is
	 * returned.
	 * 
	 * @param fieldName
	 *            the field name (1-to-1 relationship with the DB) associated
	 *            with a value
	 * @return the value based on <code>fieldName</code> or <code>null</code>
	 *         if no such value/field.
	 */
	public String get(String fieldName) {

		String result = null;

		// grab a key interator and iterate over the keys
		Iterator keys = answers.keySet().iterator();
		while (keys.hasNext()) {
			Object next_key = keys.next();

			// if the key matches the fieldName, we set the value to result.
			if (fieldName.equals((String) next_key)) {
				result = (String) answers.get(next_key);
			}
		}

		return result;
	}

	/**
	 * If an <code>Answer</code> object has only one answer, that answer will
	 * be returned. Otherwise, this function will return <code>null</code>.
	 * 
	 * @return the answer if there is only one, <code>null</code> otherwise.
	 */
	public String getFirstAnswer() {

		String result = null;

		// grab an entry Set iterator
		Iterator key_val_iter = answers.entrySet().iterator();
		if (key_val_iter.hasNext()) {

			Map.Entry tuple = (Map.Entry) key_val_iter.next();

			/*
			 * if the tuple has only one answer (since that's the behaviour of
			 * this function), then we set the result to be the value of the
			 * only entry.
			 */
			if (!key_val_iter.hasNext()) {
				result = (String) tuple.getValue();
			}
		}

		return result;
	}

	/**
	 * Returns the <code>HashMap</code> of answers. <b>Note:</b> The
	 * <code>HashMap</code> contain Strings as its keys and values.
	 * 
	 * @return the <code>HashMap</code> of answers.
	 * 
	 * @gwt.typeArgs <java.lang.String, java.lang.String>
	 */
	public HashMap getHashMap() {
		return answers;
	}

	public void setMergeable(boolean mergeable) {
		this.mergeable = mergeable;
	}

	public boolean isMergeable() {
		return mergeable;
	}

	/**
	 * Add a where clause to this answer collection
	 * 
	 * @param col
	 *            the column
	 * @param val
	 *            the value the above column must be for the where
	 */
	public void addWhereClause(String col, String val) {
		WhereClause w = new WhereClause();
		w.setColumn(col);
		w.setValue(val);
		where_clauses.add(w);
	}

	/**
	 * Return the where clauses associated with this answer collection.
	 * 
	 * @return the where clauses associated with this answer collection.
	 */
	public ArrayList getWhereClauses() {
		return where_clauses;
	}

	/**
	 * @param table_name
	 *            the table_name to set
	 */
	public void setTableName(String table_name) {
		this.tableName = table_name;
	}

	/**
	 * @return the table_name
	 */
	public String getTableName() {
		return tableName;
	}

	/**
	 * Returns whether this <code>Answer</code> is empty or not.
	 * 
	 * @return whether this <code>Answer</code> is empty or not.
	 */
	public boolean isEmpty() {

		HashMap the_answer = getHashMap();
		Iterator keys = the_answer.keySet().iterator();

		while (keys.hasNext()) {
			String key = (String) keys.next();
			if (!key.equals("ignore") && ((String) the_answer.get(key)).length() > 0) {
				return false;
			}
		}
		return true;
	}
}
